{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 54,
   "metadata": {},
   "outputs": [],
   "source": [
    "from array import array\n",
    "import reprlib\n",
    "import math\n",
    "import numbers\n",
    "import functools\n",
    "import operator\n",
    "\n",
    "\n",
    "class Vector:\n",
    "    typecode = 'd'\n",
    "\n",
    "    def __init__(self, components):\n",
    "        self._components = array(self.typecode, components)\n",
    "\n",
    "    def __iter__(self):\n",
    "        return iter(self._components)\n",
    "\n",
    "    def __repr__(self):\n",
    "        components = reprlib.repr(self._components)\n",
    "        components = components[components.find('['):-1]\n",
    "        return 'Vector({})'.format(components)\n",
    "\n",
    "    def __str__(self):\n",
    "        return str(tuple(self))\n",
    "\n",
    "    def __bytes__(self):\n",
    "        return (bytes([ord(self.typecode)]) +\n",
    "                bytes(self._components))\n",
    "\n",
    "    def __eq__(self, other):\n",
    "        return tuple(self) == tuple(other)\n",
    "    \n",
    "    def __hash__(self):\n",
    "        hashes = (hash(x) for x in self._components)\n",
    "        return functools.reduce(operator.xor, hashes, 0)\n",
    "\n",
    "    def __abs__(self):\n",
    "        return math.sqrt(sum(x * x for x in self))\n",
    "\n",
    "    def __bool__(self):\n",
    "        return bool(abs(self))\n",
    "    \n",
    "    def __len__(self):\n",
    "        return len(self._components)\n",
    "    \n",
    "    def __getitem__(self, index):\n",
    "        cls = type(self)\n",
    "        if isinstance(index, slice):\n",
    "            return cls(self._components[index])\n",
    "        elif isinstance(index, numbers.Integral):\n",
    "            return self._components[index]\n",
    "        else:\n",
    "            msg = '{cls.__name__} indices must be integers'\n",
    "            raise TypeError(msg.format(cls=cls))\n",
    "# BEGIN VECTOR_V3_GETATTR        \n",
    "    shortcut_names = 'xyzt'\n",
    "    \n",
    "    def __getattr__(self, name):\n",
    "        cls = type(self)\n",
    "        pos = cls.shortcut_names.find(name)\n",
    "        \n",
    "        if 0 <= pos < len(cls.shortcut_names):\n",
    "            return self._components[pos]\n",
    "        msg = '{.__name__!r} object has no attribute {!r}'\n",
    "        raise AttributeError(msg.format(cls, name))\n",
    "# END VECTOR_V3_GETATTR\n",
    "\n",
    "# BEGIN VECTOR_V3_SETATTR\n",
    "    def __setattr__(self, name, value):\n",
    "        cls = type(self)\n",
    "        if len(name) == 1:\n",
    "            if name in cls.shortcut_names:\n",
    "                error = 'readonly attribute {attr_name!r}'\n",
    "            elif name.islower():\n",
    "                error = \"can't set attributes 'a' to 'z' in {cls_name!r}\"\n",
    "            else:\n",
    "                error = ''\n",
    "            if error:\n",
    "                msg = error.format(cls_name=cls.__name__, attr_name=name)\n",
    "                raise AttributeError(msg)\n",
    "        super().__setattr__(name, value)\n",
    "# END VECTOR_V3_SETATTR\n",
    "\n",
    "    @classmethod\n",
    "    def frombytes(cls, octets):\n",
    "        typecode = chr(octets[0])\n",
    "        memv = memoryview(octets[1:]).cast(typecode)\n",
    "        return cls(memv)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 55,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "4.0"
      ]
     },
     "execution_count": 55,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "v1 = Vector([3, 4, 5])\n",
    "v1[1]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 56,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3"
      ]
     },
     "execution_count": 56,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "len(v1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 57,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Vector([1.0, 2.0, 3.0])"
      ]
     },
     "execution_count": 57,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "v2 = Vector(range(7))\n",
    "v2[1:4]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 58,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "__main__.Vector"
      ]
     },
     "execution_count": 58,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "v2.__class__"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 59,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(0.0, 1.0, 2.0, 3.0)"
      ]
     },
     "execution_count": 59,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "v = Vector(range(5))\n",
    "v.x, v.y, v.z, v.t"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 60,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "4"
      ]
     },
     "execution_count": 60,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "hash(v)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
